package com.chinaoly.baotou.common.utils;


import com.chinaoly.frm.common.utils.io.minio.entity.MinioFile;
import com.chinaoly.frm.common.utils.io.minio.enums.PolicyType;
import io.minio.MinioClient;
import io.minio.messages.Bucket;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.List;
import java.util.Optional;

/**
 * MinIOTemplate
 *
 * @author Cl
 */
@Slf4j
public class MinioUtil {

    private final String endPoint;

    private final String defaultBucket;

    /**
     * MinIO客户端
     */
    private final MinioClient client;

    public MinioUtil(MinioClient client, String endPoint, String defaultBucket) {
        this.endPoint = endPoint;
        this.defaultBucket = defaultBucket;
        this.client = client;
    }

    @SneakyThrows
    public void makeBucket(String bucketName) {
        if (!client.bucketExists(bucketName)) {
            client.makeBucket(bucketName);
            client.setBucketPolicy(bucketName, getPolicyType(bucketName, PolicyType.READ_WRITE));
        }
    }

    @SneakyThrows
    public Bucket getBucket(String bucketName) {
        Optional<Bucket> bucketOptional = client.listBuckets().stream().filter(bucket -> bucket.name().equals(bucketName)).findFirst();
        return bucketOptional.orElse(null);
    }

    @SneakyThrows
    public List<Bucket> listBuckets() {
        return client.listBuckets();
    }

    @SneakyThrows
    public void removeBucket(String bucketName) {
        client.removeBucket(bucketName);
    }

    @SneakyThrows
    public boolean bucketExists(String bucketName) {
        return client.bucketExists(bucketName);
    }

    @SneakyThrows
    public MinioFile putFile(MultipartFile file) {
        return putFile(defaultBucket, file.getOriginalFilename(), file);
    }

    @SneakyThrows
    public MinioFile putFile(String fileName, MultipartFile file) {
        return putFile(defaultBucket, fileName, file);
    }


    @SneakyThrows
    public MinioFile putFile(String bucketName, String fileName, MultipartFile file) {
        return putFile(bucketName, fileName, file.getInputStream());
    }

    @SneakyThrows
    public MinioFile putFile(String fileName, InputStream stream) {
        return putFile(defaultBucket, fileName, stream);
    }

    @SneakyThrows
    public MinioFile putFile(String bucketName, String fileName, InputStream stream) {
        makeBucket(bucketName);
        client.putObject(bucketName, fileName, stream, (long) stream.available(), null, null, "application/octet-stream");
        MinioFile file = new MinioFile();
        file.setName(fileName);
        file.setLink(this.fileLink(bucketName, fileName));
        return file;
    }

    @SneakyThrows
    public InputStream getFileInputStream(String fileName) {
        return getFileInputStream(defaultBucket, fileName);
    }

    /**
     * 获取文件
     *
     * @param bucketName bucket名称
     * @param fileName   文件名称
     * @return 二进制流
     */
    @SneakyThrows
    public InputStream getFileInputStream(String bucketName, String fileName) {
        log.info("minioUtil 下载附件,桶名称[{}]，文件名称[{}]",bucketName,fileName);
        return client.getObject(bucketName, fileName);
    }

    public String fileLink(String fileName) {
        return this.fileLink(defaultBucket, fileName);
    }

    public String fileLink(String bucketName, String fileName) {
        try {
            return this.endPoint.concat("/").concat(bucketName).concat("/").concat(fileName);
        } catch (Throwable var4) {
            throw var4;
        }
    }


    @SneakyThrows
    public void removeFile(String fileName) {
        removeFile(defaultBucket, fileName);
    }

    @SneakyThrows
    public void removeFile(String bucketName, String fileName) {
        client.removeObject(bucketName, fileName);
    }

    @SneakyThrows
    public void removeFiles(String bucketName, List<String> fileNames) {
        client.removeObjects(bucketName, fileNames);
    }


    /**
     * 获取存储桶策略
     *
     * @param bucketName 存储桶名称
     * @param policyType 策略枚举
     * @return String
     */
    public static String getPolicyType(String bucketName, PolicyType policyType) {
        StringBuilder builder = new StringBuilder();
        builder.append("{\n");
        builder.append("    \"Statement\": [\n");
        builder.append("        {\n");
        builder.append("            \"Action\": [\n");

        switch (policyType) {
            case WRITE:
                builder.append("                \"s3:GetBucketLocation\",\n");
                builder.append("                \"s3:ListBucketMultipartUploads\"\n");
                break;
            case READ_WRITE:
                builder.append("                \"s3:GetBucketLocation\",\n");
                builder.append("                \"s3:ListBucket\",\n");
                builder.append("                \"s3:ListBucketMultipartUploads\"\n");
                break;
            default:
                builder.append("                \"s3:GetBucketLocation\"\n");
                break;
        }

        builder.append("            ],\n");
        builder.append("            \"Effect\": \"Allow\",\n");
        builder.append("            \"Principal\": \"*\",\n");
        builder.append("            \"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("\"\n");
        builder.append("        },\n");
        if (PolicyType.READ.equals(policyType)) {
            builder.append("        {\n");
            builder.append("            \"Action\": [\n");
            builder.append("                \"s3:ListBucket\"\n");
            builder.append("            ],\n");
            builder.append("            \"Effect\": \"Deny\",\n");
            builder.append("            \"Principal\": \"*\",\n");
            builder.append("            \"Resource\": \"arn:aws:s3:::");
            builder.append(bucketName);
            builder.append("\"\n");
            builder.append("        },\n");

        }
        builder.append("        {\n");
        builder.append("            \"Action\": ");

        switch (policyType) {
            case WRITE:
                builder.append("[\n");
                builder.append("                \"s3:AbortMultipartUpload\",\n");
                builder.append("                \"s3:DeleteObject\",\n");
                builder.append("                \"s3:ListMultipartUploadParts\",\n");
                builder.append("                \"s3:PutObject\"\n");
                builder.append("            ],\n");
                break;
            case READ_WRITE:
                builder.append("[\n");
                builder.append("                \"s3:AbortMultipartUpload\",\n");
                builder.append("                \"s3:DeleteObject\",\n");
                builder.append("                \"s3:GetObject\",\n");
                builder.append("                \"s3:ListMultipartUploadParts\",\n");
                builder.append("                \"s3:PutObject\"\n");
                builder.append("            ],\n");
                break;
            default:
                builder.append("\"s3:GetObject\",\n");
                break;
        }

        builder.append("            \"Effect\": \"Allow\",\n");
        builder.append("            \"Principal\": \"*\",\n");
        builder.append("            \"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("/*\"\n");
        builder.append("        }\n");
        builder.append("    ],\n");
        builder.append("    \"Version\": \"2012-10-17\"\n");
        builder.append("}\n");
        return builder.toString();
    }
}
